/*
 * pladc.c
 *
 *  Created on: May 2, 2017
 *      Author: yuanjianen
 */


#include "pladc.h"
#include "pladc_config.h"
#include "plconfig.h"
#include "costr.h"
#include "comath.h"
#include "cobit.h"

static uint16 pladc_ad_period_common_multiple;
uint16 pladc_ad_sample_time_ms_count = 0U;

static void pladc_process_ad_data(uint16 raw, uint8 config_index)  {
	const PladcConfig *config;
	PladcData *adcinfo;
	uint16 gain_numerator;
	uint16 gain_denominator;
	uint16 ad_adjusted;
	uint16 ad_referenced;
	uint16 ad_filtered_old;
	uint16 ad_filtered;
	uint32 ad_mapped;
	int32 ad_filtered_long;

	uint8 ref_pwr_index;
	uint8 filter_coeff;

	config = &(pladc_config[config_index]);
	adcinfo = &(pladc_adc_data[config_index]);
	ref_pwr_index = config->ref_5v_power_index;
	filter_coeff = config->first_order_coeff; /*LSB: 1/256*/

	ad_adjusted = raw;
	if(ref_pwr_index < pladc_config_count) {
		ad_referenced = pladc_read_mapped(ref_pwr_index);
		if(ad_referenced != 0U) {
			ad_adjusted = (uint16)(((uint32)(raw) << 10U) / ad_referenced);
		}
	}
	adcinfo->ad_5vadj = ad_adjusted;

	ad_filtered = ad_adjusted;
	if(filter_coeff != 0U) {
		ad_filtered_old = adcinfo->ad_filtered;
		ad_filtered_long = (int32)((uint32)ad_adjusted << 8U);
		ad_filtered_long = (ad_filtered_long - (((int32)ad_adjusted - (int32)ad_filtered_old) * (int32)filter_coeff));
		if(ad_filtered_long < 0) {
			ad_filtered_long = 0U;
		}
		ad_filtered = (uint16)((uint32)ad_filtered_long >> 8U);
		if(ad_filtered > 1024U) {
			ad_filtered = 1024U;
		}
	}
	adcinfo->ad_filtered = ad_filtered;


	gain_numerator = config->gain_numerator;
	gain_denominator = config->gain_denominator;
	if((gain_numerator != gain_denominator) && (gain_numerator != 0U) && (gain_denominator != 0U)) {
		ad_mapped = (((uint32)(ad_filtered) * (uint32)gain_numerator) / (uint32)gain_denominator);
	} else {
		ad_mapped = (uint32)(ad_filtered);
	}
	ad_mapped += (uint32) pladc_config[config_index].offset;
	if(ad_mapped >= 0xFFFFU) {
		pladc_adc_data[config_index].ad_mapped = 0xFFFFU;
	} else {
		pladc_adc_data[config_index].ad_mapped = (uint16) ad_mapped;
	}
}


void pladc_init_adc(void) {
	uint8 index;
	uint8 channel;
	uint16 ad_data;
	const PladcConfig* config;

	pladc_ad_period_common_multiple = wordop_calc_period_lcm(pladc_config, pladc_config_count, PladcConfig, sample_period_ms);
	for(index = 0U; index < pladc_config_count; index++) {
		config = &(pladc_config[index]);
		channel = (uint8)(config->ad_channel);

		if((config->sample_period_ms == 0U) || (channel == INVALID_ADC_CHANNEL)) {
			continue ;
		}

		if(config->config_index_ref != NULL) {
			(*(config->config_index_ref)) = index;
		}

		ad_data = pladc_get_value_from_device(channel);
		pladc_adc_data[index].ad_raw = ad_data;
//		pladc_adc_data[index].ad_5vadj = ad_data;
//		pladc_adc_data[index].ad_filtered = ad_data;
		pladc_process_ad_data(ad_data, index);
		if(config->callback != NULL) {
			config->callback(index, &(pladc_adc_data[index]));
		}
	}
}

void pladc_sample_task(void) {
	uint8 index;
	uint8 channel;
	uint16 ad_data;
	const PladcConfig* config;

	for(index = 0U; index < pladc_config_count; index++) {
		config = &(pladc_config[index]);
		channel = (uint8)(config->ad_channel);
		if((config->sample_period_ms == 0U) || (channel == INVALID_ADC_CHANNEL)) {
			continue ;
		}

		if((pladc_ad_sample_time_ms_count % config->sample_period_ms) == 0U) {

			drsys_lock_cpu();
			ad_data = pladc_get_value_from_device(channel);
			pladc_adc_data[index].ad_raw = ad_data;
			pladc_process_ad_data(ad_data, index);
			if(config->callback != NULL) {
				config->callback(index, &(pladc_adc_data[index]));
			}
			drsys_unlock_cpu();
		}
	}
	pladc_ad_sample_time_ms_count += PLCFG_AD_SAMPLE_TASK_TIME;
	if(pladc_ad_sample_time_ms_count >= pladc_ad_period_common_multiple) {
		pladc_ad_sample_time_ms_count = 0U;
	}
}

/*
 * The AD sampling will be processed by pladc_sample_task normally,
 * but when you need the ad at once, you must call this function
 * to get the latest value.
 *
 * */
void pladc_sample_adc(uint8 config_index) {
	const PladcConfig* config;
	uint8 channel;
	uint16 ad_data;
	if(config_index < pladc_config_count) {
		config = &(pladc_config[config_index]);
		channel = (uint8)(config->ad_channel);
		ad_data = pladc_get_value_from_device(channel);
		pladc_adc_data[config_index].ad_raw = ad_data;
		pladc_process_ad_data(ad_data, config_index);
		if(config->callback != NULL) {
			config->callback(config_index, &(pladc_adc_data[config_index]));
		}
	}
}

uint16 pladc_get_value_from_device(uint16 channel){
	uint8 index;
	const PladcConfig* config;
	for(index = 0U; index < pladc_config_count; index++) {
		config = &(pladc_config[index]);
		if(config->ad_channel != channel) {
			continue ;
		}
		break;
	}
	return pladc_device_value[index];
}
